/*
 * Copyright (c) 2004-2005, Volatile-Engine All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Volatile-Engine nor the
 * names of its contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
package com.volatileengine.util;

/**
 * <p>
 * The Timer class is used to measure frames per second (FPS). Use of this class
 * is very simple; just create an instance, then call {@link #tick() tick} once
 * per frame in your application. The last FPS calculated can be retrieved using
 * {@link #getLastFPS() getLastFPS}. The number of updates per second can be set
 * using the {@link #setTicksPerSecond(int) setTicksPerSecond} method.
 * </p>
 * 
 * <p>
 * This timer also has the ability to store previous FPS values and return the
 * average value using getFPS(). The number of values stored in this history can
 * be set using {@link #setHistorySize(int) setHistorySize}.
 * </p>
 * 
 * @author ChaosDeathFish
 */
public class Timer {
	/** The number of nanoseconds in a second */
	public static final long SECOND_IN_MS = 1000;

	/** The real time of the last tick */
	private long lastRealTime;
	/** The time that the last tick was supposed to occur at */
	private long lastTickTime;
	/** The number of calls to tick() since the last FPS update */
	private int frames;

	/** The time (in nanoseconds) between FPS updates */
	private long fpsUpdatePeriod;

	/** The FPS history array */
	private float[] fpsHistory;
	/** The total of the FPS figures stored in the history */
	private float fpsTotal;
	/** The size of the FPS history (<=fpsHistory.length) */
	private int fpsHistorySize;

	/** The current FPS history entry */
	private int fpsCurrentEntry;

	/**
	 * Creates a timer that updates the FPS once per second and only keeps one
	 * FPS figure in the history.
	 */
	public Timer() {
		this(1, 1);
	}

	/**
	 * Creates a timer with the specified ticks per second and history size.
	 * 
	 * @param ticksPerSecond
	 *             the number of FPS updates per second.
	 * @param historySize
	 *             the size of the FPS history.
	 */
	public Timer(int ticksPerSecond, int historySize) {
		setTicksPerSecond(ticksPerSecond);
		setHistorySize(historySize);
		reset();
	}

	/**
	 * Resets the timer. Sets the entire FPS history back to 0, sets the last
	 * update time to the current time and resets the frame count to 0.
	 */
	public void reset() {
		lastRealTime = lastTickTime = System.currentTimeMillis();
		clearHistory(0);
		frames = 0;
	}

	/**
	 * Update the frame count. If a FPS update is due, perform the update.
	 * 
	 * @return true if the FPS was updated, false otherwise.
	 */
	public boolean tick() {
		frames++;
		long realTime = System.currentTimeMillis();

		if (realTime - lastTickTime >= fpsUpdatePeriod) {
			long time = realTime - lastRealTime;
			float fps = (SECOND_IN_MS / (float) time) * frames;
			lastTickTime += fpsUpdatePeriod;
			lastRealTime = realTime;
			frames = 0;

			fpsCurrentEntry = (fpsCurrentEntry + 1) % fpsHistorySize;
			fpsTotal -= fpsHistory[fpsCurrentEntry];
			fpsHistory[fpsCurrentEntry] = fps;
			fpsTotal += fps;

			return true;
		}

		return false;
	}

	/**
	 * Sets the number of FPS updates per second.
	 * 
	 * @param ticks
	 *             the number of FPS updates per second.
	 */
	public void setTicksPerSecond(int ticks) {
		fpsUpdatePeriod = SECOND_IN_MS / ticks;
	}

	/**
	 * Sets the size of the FPS history, clearing the history to the current
	 * average FPS.
	 * 
	 * @param size
	 *             the new FPS history size.
	 */
	public void setHistorySize(int size) {
		float fps = fpsTotal / fpsHistorySize;
		fpsHistorySize = size;
		if (fpsHistory == null || fpsHistory.length < size) {
			fpsHistory = new float[size];
		}
		clearHistory(fps);
	}

	/**
	 * Clear all entries in the FPS history to the specified FPS value.
	 * 
	 * @param fps
	 *             the FPS to update the history with.
	 */
	private void clearHistory(float fps) {
		fpsTotal = fps * fpsHistorySize;
		for (int i = 0; i < fpsHistorySize; i++) {
			fpsHistory[i] = fps;
		}
		fpsCurrentEntry = 0;
	}

	/**
	 * @return the last FPS value calculated.
	 */
	public float getLastFPS() {
		return fpsHistory[fpsCurrentEntry];
	}

	/**
	 * @return the average FPS value over the FPS history.
	 */
	public float getFPS() {
		return fpsTotal / fpsHistorySize;
	}

	/**
	 * @return the inverse of getFPS().
	 */
	public float getTimePerFrame() {
		if (fpsTotal == 0) {
			return 0;
		}
		return fpsHistorySize / fpsTotal;
	}
}
